-- | Module:        Hexena.OptParse
--   Stability:     Experimental
--   Portability:   Portable
--   
--   The command line multimode option parser for Hexena.
--
--   LANGUAGE Pragma's DeriveDataTypeable and RecordWildCards
--   are required to use the CmdArgs library.  Well just
--   DeriveDataTypeable but RecordWildCards definitely 
--   saves on your typing, and hence possible mistakes.
--
-- http://community.haskell.org/~ndm/darcs/cmdargs/cmdargs.htm
-- http://hackage.haskell.org/packages/archive/cmdargs/latest/doc/html/System-Console-CmdArgs-Implicit.html#v:args

{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}
module Hexena.OptParse 
    ( optionHandler
    , rootMode
    ) where

import Control.Monad (when)
import System.Console.CmdArgs

import Hexena.OptParse.Internal ( _PROGRAM_NAME
                                , _PROGRAM_VERSION
                                , _PROGRAM_INFO
                                , _PROGRAM_ABOUT
                                )
                                 
import qualified Hexena.Cmds.BlockDraw (run)
import qualified Hexena.Cmds.BloomGenerate (run)
import qualified Hexena.Cmds.BloomLookup (run)
import qualified Hexena.Cmds.CalcCV (run)
import qualified Hexena.Cmds.CalcBlockEntropy (run)
import qualified Hexena.Cmds.ClassifySVM (run)
import qualified Hexena.Cmds.EncapsulateEXE (run)
import qualified Hexena.Cmds.EntropyCalc (run)
import qualified Hexena.Cmds.EntropyXPM (run)
import qualified Hexena.Cmds.GenerateReport (run)
import qualified Hexena.Cmds.GetSVMData (run)
import qualified Hexena.Cmds.ReadMZ (run)
import qualified Hexena.Cmds.HexView (run)

-- | Record type defined to capture information recorded from
--   the command line.
data HexenaOpts = Block_Draw { src :: FilePath }
                | Bloom_Lookup { inputs :: [FilePath] }
                | Bloom_Generate { inputs :: [FilePath], ngrams :: Int,  modulo::Int }
                | Calc_CV { src :: FilePath }
                | Classify_SVM { src :: FilePath, modelFile :: Maybe FilePath }
                | Encapsulate_Exe { inputs :: [FilePath], killBin :: Bool }
                | Entropy_Calc { src :: FilePath
                                 , mean :: Bool }
                | Entropy_XPM { src :: FilePath }
                | Generate_Report { src :: FilePath }
                | Get_SVM_Data { inputs :: [FilePath]
                               , classifier :: Int }
                | Read_MZ { inputs :: [FilePath] }
                | Hex_View { src :: FilePath }
                | Calc_BlockEntropy { src :: FilePath }
                  deriving (Data, Typeable, Show, Eq)

block_draw :: HexenaOpts
block_draw = Block_Draw
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

calc_blockentropy :: HexenaOpts
calc_blockentropy = Calc_BlockEntropy
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

bloom_generate :: HexenaOpts
bloom_generate = Bloom_Generate
    { inputs = def &= args
    , ngrams = 3 &= name "grams" &= help "which n-grams to use (3=triple, 4=quadruple, etc.)"
    , modulo = 69105 &= name "modulo" &= help "modulo (m) or number of bits for bloom filter"
    }
    &= details [ "The current defaults are 3-grams, modulo=69105, and three hashing functions (k=3)."
               , ""
               , "Examples:"
               , "hexena bloom-generate -g 5 -m 16430748 *.exe > exes.bloom.5-gram.iff"
               ]


bloom_test :: HexenaOpts
bloom_test = Bloom_Lookup
    { inputs = def &= args
    }
    &= details [ "Currently three hashing functions (k=3) are used. This function uses the parameters stored in the data file."
               , ""
               , "Examples:"
               , "hexena bloom-lookup unknown.exe < exes.bloom.5-gram.iff"
               ]

calc_cv :: HexenaOpts
calc_cv = Calc_CV
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

classify_svm :: HexenaOpts
classify_svm = Classify_SVM
    { src = def &= typ "INPUT" &= argPos 0
    , modelFile = Nothing &= name "model" &= help "path to a model file"
    }
    &= details [ "Examples:"
               , "hexena classify-svm filename.hexena"
               ]

encapsulate_exe :: HexenaOpts
encapsulate_exe = Encapsulate_Exe
    { inputs = def &= args
    , killBin = True &= name "kill-binary" &= help "binary should be removed from hexena file, default is true"
    }
    &= details [ "Examples:"
               , "TODO"
               ]
               
entropy_calc :: HexenaOpts
entropy_calc = Entropy_Calc
    { src = def &= typ "INPUT" &= argPos 0
    , mean = def &= name "m" &= help "Also calculate the mean value"
    }
    &= details [ "Examples:"
               , "TODO"
               ]

entropy_xpm :: HexenaOpts
entropy_xpm = Entropy_XPM
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

generate_report :: HexenaOpts
generate_report = Generate_Report
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

get_svm_data :: HexenaOpts
get_svm_data = Get_SVM_Data
    { inputs = def &= args
    , classifier = -1 
    }
    &= details [ "Examples:"
               , "TODO"
               ]


               
read_mz :: HexenaOpts
read_mz = Read_MZ
    { inputs = def &= args
    }
    &= details [ "Examples:"
               , "TODO"
               ]
               
hex_view :: HexenaOpts
hex_view = Hex_View
    { src = def &= typ "INPUT" &= argPos 0
    }
    &= details [ "Examples:"
               , "TODO"
               ]

rootMode :: Mode (CmdArgs HexenaOpts)
rootMode = cmdArgsMode $ modes [ block_draw
                               , bloom_test
                               , bloom_generate
                               , calc_blockentropy
                               , calc_cv
                               , classify_svm
                               , encapsulate_exe
                               , entropy_calc
                               , entropy_xpm
                               , generate_report
                               , get_svm_data
                               , read_mz
                               , hex_view
                               ]
           &= versionArg [explicit, name "version", name "v", summary _PROGRAM_INFO]
           &= summary (_PROGRAM_INFO)
           &= help _PROGRAM_ABOUT
           &= helpArg [explicit, name "help", name "h"]
           &= program _PROGRAM_NAME
    
-- | Option Handlers are the the change to weed out ugly, malformed or invalid data.
--   Option Handlers could be considered the warden, it would be a good place 
--   in the future to put a warden which would do considerably more src
--   checking before choosing to process.
--
--   Then sanitized arguments get passed to the program via 'exec'.
optionHandler :: HexenaOpts -> IO ()
optionHandler opts@Block_Draw{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Bloom_Lookup{..} = do
    when (null inputs) $ putStrLn "warning: no files given"
    exec opts
optionHandler opts@Bloom_Generate{..} = do
    when (null inputs) $ putStrLn "warning: no files given"
    exec opts
optionHandler opts@Calc_BlockEntropy{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Calc_CV{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Classify_SVM{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Encapsulate_Exe{..} = do
    when (null inputs) $ putStrLn "warning: no files given"
    exec opts
optionHandler opts@Entropy_Calc{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Entropy_XPM{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Generate_Report{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
optionHandler opts@Get_SVM_Data{..} = do
    when (null inputs) $ putStrLn "warning: no input files given"
    when (classifier `notElem` [-1, 1]) $ putStrLn "classifier for SVM must be either -1 (malicious) or 1 (benign)"
    exec opts
optionHandler opts@Read_MZ{..} = do
    when (null inputs) $ putStrLn "warning: no input files given"
    exec opts
optionHandler opts@Hex_View{..} = do
    when (null src) $ putStrLn "warning: src is blank"
    exec opts
    
-- | exec is the workhorse once optionHandler has properly transformed
--   the src data.
exec :: HexenaOpts -> IO ()
exec opts@Block_Draw{..} = Hexena.Cmds.BlockDraw.run src
exec Bloom_Lookup{..} = Hexena.Cmds.BloomLookup.run inputs
exec Bloom_Generate{..} = Hexena.Cmds.BloomGenerate.run inputs ngrams modulo
exec Calc_CV{..} = Hexena.Cmds.CalcCV.run src
exec Calc_BlockEntropy{..} = Hexena.Cmds.CalcBlockEntropy.run src
exec Classify_SVM{..} = Hexena.Cmds.ClassifySVM.run src modelFile
exec Encapsulate_Exe{..} = Hexena.Cmds.EncapsulateEXE.run inputs killBin
exec Entropy_Calc{..} = Hexena.Cmds.EntropyCalc.run src mean
exec Entropy_XPM{..} = Hexena.Cmds.EntropyXPM.run src
exec Generate_Report{..} = Hexena.Cmds.GenerateReport.run src
exec Get_SVM_Data{..} = Hexena.Cmds.GetSVMData.run classifier inputs
exec Read_MZ{..} = Hexena.Cmds.ReadMZ.run inputs
exec Hex_View{..} = Hexena.Cmds.HexView.run src
